package fag.core.geometry;

import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.io.Serializable;

import fag.core.collision.Hitbox;

/**
 * Classe vecteur 2D.
 * @author lumz
 */
public class Vector2D extends Hitbox implements Cloneable, Serializable, Comparable {

	private static final long serialVersionUID = -6215075151203896778L;
	public double x = 0;
	public double y = 0;
	
	/**
	 * Constructeur par défault.
	 * Le vecteur est intialisé à 0, 0.
	 */
	public Vector2D() {}
	
	/**
	 * Constructeur avec initialisation.
	 * @param x Coordonnée x.
	 * @param y Coordonnée y.
	 */
	public Vector2D(double x, double y) {
		this.x = x;
		this.y = y;
	}
	
	/**
	 * Calculer la somme de deux vecteurs.
	 * @param vector Vecteur à additionner.
	 * @return La somme des deux vecteurs.
	 */
	public Vector2D add(Vector2D vector) {
		return new Vector2D(this.x + vector.x, this.y + vector.y);
	}
	
	@Override
	public Vector2D clone() {
		return (Vector2D) super.clone();
	}
	
	/**
	 * Calcule le déterminant.
	 * @param vector Vecteur du déterminant.
	 * @return Le déterminant.
	 */
	public double det(Vector2D vector) {
		return this.x * vector.y - this.y * vector.x;
	}
	
	/**
    * Fonction détectant si un 3ème point se situe à la gauche d'un vecteur ou non
    * @param
    * Vector2DD a : le premier point du vecteur
    * Vector2DD b : le second point du vecteur
    * Vector2DD c : le point à comparer
    * @return boolean : true si le point est à gauche ou sur le même axe (false le cas échéant)
    */
   public static boolean det(Vector2D a, Vector2D b, Vector2D c) {
       return ((b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x)) >= 0;
   }
	
	/**
	 * Calcule le produit scalaire.
	 * @param vector Vecteur du produit scalaire.
	 * @return Le produit scalaire.
	 */
	public double dot(Vector2D vector) {
		return this.x * vector.x + this.y * vector.y;
	}
	
	@Override
	public Rectangle2D getBounds() {
		return new Rectangle2D(x, y, 0, 0);
	}
	
	/**
	 * Calcule la norme.
	 * @return La norme du vecteur.
	 */
	public double length() {
		return Math.sqrt(x * x + y * y);
	}
	
	/**
	 * Calcule la multiplication par un scalaire.
	 * @param k Multiplicateur scalaire du vecteur.
	 * @return Le résultat de la multiplication scalaire.
	 */
	public Vector2D mul(double k) {
		return new Vector2D(this.x * k, this.y * k);
	}
	
	/**
	 * Normale du vecteur.
	 * @param winding Sens de la normale.
	 * @return La normale de ce vecteur dans le sens spécifié.
	 */
	public Vector2D normal(Winding winding) {
	
		switch(winding) {
		case CLOCKWISE :
			return new Vector2D(y, -x);
		default :
			return new Vector2D(-y, x);
		}
	}
	
	/**
	 * Normalisation de vecteur.
	 * @return Le résultat de la normalisation.
	 */
	public Vector2D normalize() {
		
		double length = length();
		
		return new Vector2D(this.x / length, this.y / length);
	}
	
	/**
	 * Setter du vecteur. 
	 * @param vector Le nouveau vecteur.
	 */
	public void set(Vector2D vector) {
		x = vector.x;
		y = vector.y;
	}
	
	/**
	 * Setter du vecteur.
	 * @param x La nouvelle coordonnée x.
	 * @param y La nouvelle coordonnée y.
	 */
	public void set(double x, double y) {
		this.x = x;
		this.y = y;
	}
	
	/**
	 * Accesseurs
	 */
	public double getX() { return this.x; }
    public double getY() { return this.y; }
	
	/**
	 * Soustraction de vecteurs.
	 * @param vector Le vecteur à soustraire.
	 * @return La soustraction des deux vecteurs.
	 */
	public Vector2D sub(Vector2D vector) {
		return new Vector2D(this.x - vector.x, this.y - vector.y);
	}
	
	/**
	 * Implémentation de supportMapping pour Collidable.
	 */
	public Vector2D supportMapping(Vector2D d) {
		return this.clone();
	}
	
	@Override
	public String toString() {
		return new String() + "Vector2D(" + x + ", " + y + ")";
	}
	
	@Override
	public Vector2D transform(AffineTransform transformation) {
		
		Point2D.Double source = new Point2D.Double(x, y);
		Point2D.Double result = new Point2D.Double();
		
		transformation.transform(source, result);
		
		return new Vector2D(result.x, result.y);
	}

	@Override
	public int compareTo(Object o) {
        Vector2D other = (Vector2D) o;
        double otherX = other.getX();
        if (this.x < otherX)  return -1;
        else if(this.x == otherX) return 0;
        else return 1;
    }
}
